package jp.co.tms.internal;

import java.util.ArrayList;

import jp.co.tms.internal.data.ITaskQueue;
import jp.co.tms.internal.data.TaskQueue;
import jp.co.tms.internal.runserver.RunServerManager;
import jp.co.tms.internal.task.IKillTask;
import jp.co.tms.internal.task.IRunTask;
import jp.co.tms.internal.task.ITask;
import jp.co.tms.internal.webserver.WebServerManager;


public class TaskDistributeEngine {
	
	private static final int TIME_WAIT = 4000;
	
	private static TaskDistributeEngine _manager = new TaskDistributeEngine();
	
	private ITaskQueue _webServerInputQueue = new TaskQueue();
	
	private ITaskQueue _webServerOutputQueue = new TaskQueue();
	
	private ITaskQueue _runServerInputQueue = new TaskQueue();
	
	private ITaskQueue _runServerOutputQueue = new TaskQueue();
	
	private ArrayList<ITask> _taskHeap = new ArrayList<ITask>();
	
	private RunServerManager _runServerManager;
	
	private WebServerManager _webServerManager;
	
	private TaskDistributeEngine(){
	}
	
	public static TaskDistributeEngine getInstance(){
		return _manager;
	}
	
	public void run(){
		//
		// Create Web Server Manager and set the input and output queue.
		_webServerManager = new WebServerManager(
				_webServerOutputQueue, _webServerInputQueue);
		//
		//  Create Run Server Manager and set the input and output queue.
		_runServerManager = new RunServerManager(
				_runServerInputQueue, _runServerOutputQueue);
		//
		// Start Web Server Monitor.
		if (_webServerManager.start() != true){
			return;
		}
		//
		// Start Run Server Monitor.
		if (_runServerManager.start() != true){
			return;
		}
		//
		// Start Loop.
		while(true){
			
			try{
				//
				// Get a new task from input queue and distribute it.
				//
				ITask task = getNextInputTask();
				if ( task != null ){	
					// 1. remove the task from the heap. but if it is not there or no on PEND status.leave it.
					if ( task instanceof IKillTask ){
						IKillTask killTask = (IKillTask)task;
						IRunTask runningTask = getRunningTaskById(killTask.getId());
						
						// if the task is still in heap with PEND status,just remove it.
						if ( runningTask != null 
								&& runningTask.getStatus() == IRunTask.STAT_PEND ) {
							_taskHeap.remove(runningTask);
						}
						
						// if the task already been move to input queue then you need remove it too.
						if ( runningTask != null
								&& runningTask.getStatus() == IRunTask.STAT_ADDED ) {
							if ( _runServerInputQueue.contails(runningTask) ){
								_runServerInputQueue.remove(runningTask);
							}							
						}
					}
					// 2. copy the task into run server input queue and reset the status.
					//    but if there is no idle cpu, you must wait.
					//    [POINT] do not set the task'status form PEND to RUNNING here.
					else if ( task instanceof IRunTask 
							&& _runServerManager.hasIdleCpu()){
						IRunTask addedTask = (IRunTask)task;
						if ( _runServerInputQueue.offer(addedTask) ){
							addedTask.setStatus(IRunTask.STAT_ADDED);
						}
					}
				}
		
				//
				// Check the task from the run server and move them to web server.
				//
				ITask fbTask = getOutputTask();
				if ( fbTask != null ){
					if ( fbTask instanceof IRunTask ){
						IRunTask addedTask = (IRunTask)fbTask;
						if ( _taskHeap.contains(addedTask) ){
							addedTask.setStatus(IRunTask.STAT_END);
							if ( _webServerOutputQueue.offer(addedTask) ){
								_taskHeap.remove(addedTask);
							}
						}
					}
				}
				
				//
				// If There is no task in the queue, sleep for TIME_WAIT / 1000 seconds.
				Thread.sleep(TIME_WAIT);			
			}
			catch (InterruptedException e){
				e.printStackTrace();
			}	
		}
	}
	
	protected ITask getNextInputTask(){
		//
		// Move All the task from queue to heap.
		do {	
			ITask task = _webServerInputQueue.poll();	
			if ( task == null )
				break;
			
			if ( task instanceof IRunTask ){
				IRunTask runTask = (IRunTask)task;
				runTask.setStatus(IRunTask.STAT_PEND);
			}
			
			_taskHeap.add(task);
			
		} while ( true) ;
		
		//
		// Choose a high priority task from heap.
		return getHighPriorityTask();
	}
	
	protected IRunTask getRunningTaskById(int id){
		for (ITask task : _taskHeap ){
			if ( task instanceof IRunTask ){
				if ( task.getId() == id ){
					return (IRunTask)task;
				}
			}
		}
		
		return null;
	}
	
	protected ITask getHighPriorityTask(){
		//
		//0. no task in heap.
		if ( _taskHeap.size() == 0 )
			return null;
		
		//
		//1. KillTask or TerminateTask has the highest priority.
		for ( ITask task : _taskHeap ){
			if ( task instanceof IKillTask ){
				return task;
			}
		}		
		
		//
		//2. pick the highest one with pend status. 
		//   
		IRunTask objTask = null;
		int highestPriority = 0;
		for ( ITask task : _taskHeap ){
			if ( task instanceof IRunTask ){
				IRunTask addedTask = (IRunTask)task;
				if ( addedTask.getStatus() == IRunTask.STAT_PEND ){
					int priority = addedTask.getPriority();
					if ( priority >= highestPriority ){
						highestPriority = priority;
						objTask = addedTask;
					}
				}
			}
		}
		
		return objTask;
	}
	
	//
	//
	protected ITask getOutputTask(){
		return _runServerOutputQueue.poll();
	}
}